/* 
 *	Copyright (C) 2009-2010 miaohua
 *	http://code.google.com/p/lrcshower/
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "Stdafx.h"
#include <string>
#include "Config.h"
#include "SimpleFileDownloader.h"
#include "Exceptions.h"
#include "HelperFunctions.h"
using namespace std;

const int CSimpleFileDownloader::m_CacheSize = 512*1024;

CSimpleFileDownloader::CSimpleFileDownloader()
{
     m_Internet =  InternetOpen(_T("AutoUpdateAgent"), INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
     if( m_Internet == NULL )
         throw InternetInitError("Fail to call InternetOpen");
} 

bool CSimpleFileDownloader::GetPageCache()
{
     if( !m_PageCache.get() )
         m_PageCache = auto_ptr<char>(new char[m_CacheSize]);  //512k
     return true;
}

bool CSimpleFileDownloader::StartFileDownload(const tstring & Url, tstring & FileFullPath)
{
     HINTERNET InternetSession = GetSession(Url);
	 if ( !InternetSession )
     {
#ifdef _DEBUG
          _Dbg_Printf(_T("Error to get session \n"));
#endif
          return false;
     }

     return Download(InternetSession, FileFullPath);
}

bool CSimpleFileDownloader::StopFileDownload()
{
     return true;
}

CSimpleFileDownloader::~CSimpleFileDownloader()
{
     if( m_Internet ) 
	     InternetCloseHandle(m_Internet);
}

HINTERNET CSimpleFileDownloader::GetSession(const tstring &Url)
{
	 // Canonicalization of the URL converts unsafe characters into escape character equivalents
	 TCHAR CanonicalURL[1024];
	 DWORD nSize = sizeof(CanonicalURL)/sizeof(CanonicalURL[0]);
     if( InternetCanonicalizeUrl(Url.c_str(), CanonicalURL, &nSize, ICU_BROWSER_MODE) == FALSE )
         return NULL;
    	
	 DWORD options = INTERNET_FLAG_NEED_FILE|INTERNET_FLAG_HYPERLINK|INTERNET_FLAG_RESYNCHRONIZE|INTERNET_FLAG_RELOAD;
	 HINTERNET hSession = InternetOpenUrl(m_Internet, CanonicalURL, NULL, NULL, options, 0);

	 return hSession;
}

bool CSimpleFileDownloader::Download(HINTERNET InternetSession, tstring & FileFullPath)
{ 
     if( FileFullPath.empty() )  return Download2Mem(InternetSession, FileFullPath);
     else return Download2File(InternetSession, FileFullPath);
}

bool CSimpleFileDownloader::Download2File(HINTERNET hSession, const tstring & localFile)
{	
	 BYTE pBuf[MAX_BUF_LEN];
	 DWORD dwReadSizeOut, dwTotalReadSize = 0;

     HANDLE hFile = CreateFile(localFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);        
	 if( hFile == INVALID_HANDLE_VALUE ) return false;

	 do {
		   DWORD dwWriteSize, dwNumWritten;
		   BOOL bRead = InternetReadFile(hSession, pBuf, MAX_BUF_LEN, &dwReadSizeOut);
		   dwWriteSize = dwReadSizeOut;

		   if( bRead && dwReadSizeOut > 0 )
           {
		       dwTotalReadSize += dwReadSizeOut;
			   WriteFile(hFile, pBuf, dwWriteSize, &dwNumWritten, NULL); 
			   // File write error
			   if( dwWriteSize != dwNumWritten )
               {
				    CloseHandle(hFile);					
				    return false;
			   }
		   } else {
		       if( !bRead )
			   {
			       // Error
				   CloseHandle(hFile);	
				   return false;
			   }			
			   // Nothing to read
               break;
		   }
	 } while(true);

     // Close file handler
	 CloseHandle(hFile);
     return true;
}

bool CSimpleFileDownloader::Download2Mem(HINTERNET hSession, tstring & MemFile)
{
	 BYTE pBuf[MAX_BUF_LEN];
	 DWORD dwReadSizeOut, dwTotalReadSize = 0;

     // Get memory cache zone
     GetPageCache();
     char * Cache = m_PageCache.get();
     if( !Cache ) return false;

	 do {
		    BOOL bRead = InternetReadFile(hSession, pBuf, MAX_BUF_LEN, &dwReadSizeOut);
            if( bRead && dwReadSizeOut > 0 )
            {
                memcpy(Cache, pBuf, dwReadSizeOut);
                Cache += dwReadSizeOut;
                dwTotalReadSize += dwReadSizeOut;
		    } else {
			    if( !bRead )  // Check for internet read error
				    return false;
			    break;        // It's OK, we read over, so break it
		    }
	 } while(true);

      //Append the null terminal
      *Cache = '\0';

#ifdef UNICODE
      MultiBytes2WildStr(m_PageCache.get(), MemFile);
#else
      MemFile = m_PageCache.get();
#endif

      return true;
}
